El conjunto de datos (dataset) Iris fue introducido por Ronald Fisher en su paper de 1936, The use of multiple measurements in taxonomic problems (El uso de medidas múltiples en problemas taxonómicos). https://archive.ics.uci.edu/ml/datasets/iris
El dataset mide el largo y ancho de sépalos y pétalos de 3 tipos de flores: Iris setosa, Iris virginica e Iris versicolor.
from IPython.display import Image
Image("species.jpg", width=650, height=650)
Image("measurements.jpg", width=450, height=450)
Habitualmente la data se representa mediante tablas, donde cada columna es una característica de la data y cada fila es un registro.
Cada característica es una medición distinta de las demas. Y digamos, por ejemplo, que cada registro representa los datos medidos en un tiempo determinado, por ejemplo, la primera fila a la 1:00pm, la segunda 1:10pm, la tercera 1:15pm, etc.
Pasos:
import pandas as pd #1
iris = pd.read_csv("iris.csv") #2
print('Filas y columnas', iris.shape) #3
iris.head(10) #4
Veamos sus características (columnas) :
sepal_length, sepal_width, petal_length, petal_width
Usaremos distintos gráficos para analizar las distribución de cada una de las columnas.
Los histogramas son gráficos en forma de barras, que muestran la frecuencia de un determinado intervalo de valores, es usado para variables continuas.
Image("explain_histograms.png", width=650, height=650)
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
hist = iris.plot(kind='hist', figsize=(10, 10), subplots=True, sharex=False)
_ = plt.xlabel("Values")
_ = iris.plot(kind='density', figsize=(10, 10), subplots=True, sharex=False)
Sirven también para ver la distribución de los datos, pero su utilidad se enfoca en ver si hay valores atípicos o outliers, ver la dispersión de los valores y analizar los datos del histograma en una sola dimensión.
Image("explain_boxplots.png", width=550, height=550)
Image("explain_boxplots2.png", width=350, height=350)
_ = iris.plot(kind='box',figsize=(10, 10), vert=False)
_ = plt.xlabel("Values")
plt.figure(figsize=(10,10))
_ = sns.boxplot(x="species", y="sepal_length", data=iris)
_ = sns.stripplot(x="species", y="sepal_length", data=iris, jitter=True, edgecolor="gray", size=5)
plt.figure(figsize=(10,10))
_ = sns.boxplot(x="species", y="sepal_width", data=iris)
_ = sns.stripplot(x="species", y="sepal_width", data=iris, jitter=True, edgecolor="gray")
_ = sns.FacetGrid(iris, hue="species", size=6) \
.map(sns.distplot, "sepal_length") \
.add_legend()
_ = sns.FacetGrid(iris, hue="species", size=6) \
.map(sns.distplot, "sepal_width") \
.add_legend()
Con los siguientes gráficos podemos observar cúmulos o grupos definidos, donde cada grupo es una clase. Por tanto, el problema trata de clasificación, especificamente, clasificación múltiple supervisada.
_ = sns.pairplot(iris, hue='species', size=4)
Es una manera de expresar un dataset, de dimensión alta, en grupos fácilmente identificables.
from pandas.tools.plotting import andrews_curves
plt.figure(figsize=(10,10))
_ = andrews_curves(iris, "species")
setosa = iris.query("species == 'setosa'")
virginica = iris.query("species == 'virginica'")
versicolor = iris.query("species == 'versicolor'")
f, ax = plt.subplots(figsize=(12, 12))
ax.set_aspect("equal")
ax = sns.kdeplot(setosa.sepal_width, setosa.sepal_length,
cmap="Blues", shade=True, shade_lowest=False)
ax = sns.kdeplot(virginica.sepal_width, virginica.sepal_length,
cmap="Reds", shade=True, shade_lowest=False)
ax = sns.kdeplot(versicolor.sepal_width, versicolor.sepal_length,
cmap="Greens", shade=True, shade_lowest=False)
blue = sns.color_palette("Blues")[-2]
red = sns.color_palette("Reds")[-2]
green = sns.color_palette("Greens")[-2]
_ = ax.text(2.5, 8.2, "virginica", size=16, color=red)
_ = ax.text(3.8, 4.5, "setosa", size=16, color=blue)
_ = ax.text(3.2, 5.5, "versicolor", size=16, color=green)
Muestra la correlación (directa o inversa) que existen entre las características o columnas, excepto la variable de respuesta. Esto sirve para ver si es posible resumir columnas (reducir), aumentar o combinarlas con el objetivo de reducir el tiempo de entrenamiento, eliminar redundancia de datos y mejorar el performance del modelo.
%%time
from collections import OrderedDict
class_name = 'species'
features = iris.ix[:, iris.columns != class_name]
dictFeatures = OrderedDict(features)
features = dictFeatures.keys()
features.append('species')
def plot_heatmap(df):
fig, axes = plt.subplots(figsize=(10,10))
_ = sns.heatmap(df, annot=True)
# plt.show()
# plt.close()
plot_heatmap(iris[features].corr(method='pearson'))
Verificamos la cantidad de valores NaN : Not a Number
from __future__ import division
print 'filas sin valores nan: ', len(iris.species.dropna())
print 'filas totales: ', len(iris.species)
print(iris.groupby('species').size())
Feature Selection es un proceso donde se selecciona automáticamente las características que contribuyen en mayor medida a la variable a predecir.
Reduces Overfitting: El modelo funcionará bien con data nunca vista.
Improves Accuracy: Datos menos engañosos significa una mejora en la exactitud del modelo.
Reduces Training Time: Menos data significa que los algoritmos entrenarán más rapido.
Selecciona las columnas que tengan una relación fuerte con la variable a predecir.
Para regresión: f_regression
Para classificación: chi2 or f_classif
En este caso de clasificación, se realiza el test chi square, el cual mide la dependencia entre variables estocásticas, este test remueve las características más probables a ser independientes, por lo tanto, irrelevantes para la clasificación.
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
class_name = 'species'
X = iris.ix[:, iris.columns != class_name]
y = iris[class_name]
test = SelectKBest(score_func=chi2, k=4)
fit = test.fit(X, y)
print fit.scores_
print
print X.head()
Los Bagged decision trees como lo son Random Forest y Extra Trees pueden ser usados para estimar la importancia de caracteristicas. Asi como tambien, el algoritmo Gradient Boosting.
from sklearn.model_selection import cross_val_score
def modelfit(alg, dtrain, predictors, performCV=True, printFeatureImportance=True, cv_folds=5):
alg.fit(dtrain[predictors], dtrain['species'])
dtrain_predictions = alg.predict(dtrain[predictors])
dtrain_predprob = alg.predict_proba(dtrain[predictors])[:,1]
if printFeatureImportance:
feat_imp = pd.Series(alg.feature_importances_, predictors).sort_values(ascending=False)
feat_imp.plot(kind='bar', title='Feature Importances', figsize=(14.,7.))
plt.ylabel('Feature Importance Score')
plt.show()
plt.close()
return feat_imp
%%time
from sklearn.ensemble import ExtraTreesClassifier
target = 'species'
predictors = [x for x in iris if x not in [target]]
model = ExtraTreesClassifier(random_state=10)
fi = modelfit(model, iris, predictors)
print fi.head(6)
%%time
from sklearn.ensemble import GradientBoostingClassifier
target = 'species'
predictors = [x for x in iris if x not in [target]]
model = GradientBoostingClassifier(random_state=10)
fi = modelfit(model, iris, predictors)
print fi.head(6)
En este apartado se aprenderá cómo evaluar un algoritmo, mediante diferentes técnicas de estimacion:
Esta técnica es usada para grandes datasets, donde el entrenamiento tenga un coste alto
en recursos y tiempo. Por contra, esta técnica puede tener inducir a una alta varianza, es decir, las diferencias entre la data de entrenamiento (training) y de prueba (testing) pueden ser significativas a la hora de estimar la precisión del modelo.
Lo más comun es dividir la data en: 67% para el training y 33% para el testing.
Image("split_train_test.png", width=450, height=450)
%%time
from sklearn.linear_model import LogisticRegression
from sklearn import model_selection
test_size = 0.33
seed = 7
X_train, X_test, Y_train, Y_test = model_selection.train_test_split(X, y,
test_size=test_size, random_state=seed)
model = LogisticRegression()
model.fit(X_train, Y_train)
result = model.score(X_test, Y_test)
print("Accuracy: %.3f%%") % (result*100.0)
El seed o semilla es fijo para tener el mismo conjunto de datos en todo el proceso, de tal manera, que cuando se haga pruebas usando otro algoritmo, se pueda usar la misma dataset.
Esta técnica divide la data en k conjuntos, de tal manera que se entrena con los k-1 folds(pliegues) y se testea con el sobrante, luego se hace el mismo procedimiento escogiendo otros k-1 . Este procedimiento se realiza hasta que cada fold es testeado. Luego de tener las k puntuaciones de rendimiento se puede resumir usando la media o la desviación estandar. Este resultado es más fiable en el rendimiento del algoritmo cuando se presenta una nueva data. Es necesario que la elección de k permita que las muestras sean lo suficientemente grande, lo valores más comúnes para k son 3, 5, 10.
Image("K-fold_cross_validation.jpg", width=650, height=650)
%%time
from sklearn.cross_validation import KFold
from sklearn.model_selection import cross_val_score
num_folds = 10
num_instances = len(X)
seed = 7
kfold = KFold(n=num_instances, n_folds=num_folds,
random_state=seed)
model = LogisticRegression()
results = cross_val_score(model, X, y, cv=kfold)
#model.fit(X_train, Y_train)
#result = model.score(X_test, Y_test)
print("Accuracy: mean : %.3f%%, SD : %.3f%%") % (results.mean()*100.0, results.std()*100.0)
Esta técnica trabaja con k = len(X), con el objetivo de estimar con una mayor precisión el modelo para datos nuevos. Como desventaja, es computacionalmente costoso que la técnica cross validation y su varianza es alta.
%%time
from sklearn.cross_validation import LeaveOneOut
num_instances = len(X)
loocv = LeaveOneOut(n=num_instances)
model = LogisticRegression()
results = cross_val_score(model, X, y, cv=loocv)
#model.fit(X_train, Y_train)
#result = model.score(X_test, Y_test)
print("Accuracy: mean : %.3f%%, SD : %.3f%%") % (results.mean()*100.0, results.std()*100.0)
En esta parte se verá las diferentes métricas para evaluar la calidad de las predicciones de un modelo de ML. Para la evaluación se usará un k=10 en todas las datasets.
$$ \text{metric} = \frac{\text{# correct predictions}}{\text{# total predictions}}$$
Es recomendable cuando solo se tiene un número igual de observaciones de cada clase.
num_folds = 10
num_instances = len(X)
num_samples = 10
seed = 7
kfold = KFold(n=num_instances, n_folds=num_folds,
random_state=seed)
model = LogisticRegression()
scoring = 'accuracy'
results = cross_val_score(model, X, y,
cv=kfold, scoring=scoring)
print("Accuracy: mean : %.3f (%.3f)") % (results.mean(), results.std())
Es una métrica usada para clases que no están balanceadas, por ejemplo, en el dataset Iris tenemos 50 filas de cada especie las cuales se dice que esta balanceadas, pero en muchos otros problemas las clases estan desbalanceadas, por tanto, esta métrica permite saber específicamente cómo se comporta el modelo clase por clase.
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report
model.fit(X_train, Y_train)
predictions = model.predict(X_test)
cm = confusion_matrix(Y_test, predictions)
print('Cantidad de valores de prueba: ', len(Y_test))
df_cm = pd.DataFrame(cm, y.unique(),
y.unique())
ax = plt.axes()
_ = sns.set(font_scale=1.4)
_ = sns.heatmap(df_cm, annot=True,annot_kws={"size": 16}, ax=ax)
El Pipeline es una manera de juntar transformadores (p.e, Scaler, SelectKbest) y estimadores (ExtraTrees, Logistic Regression) en un solo flujo, de tal manera que cada eslabon del pipeline se ejecute secuencialmente hasta lograr el modelo predictivo. Por ejemplo, se puede poner en un flujo los Transformers: Scale, Normalization, etc, y en otro pipeline, transformers para features selections: univariate, pca, etc. y en otro pipeline poner estimadores: PCA, k-means, etc. Entonces con la función FeatureUnion, podemos unir esos tres pipelines para crear un pipeline total, que realiza todos esos pasos como uno solo. Esto ayuda a evitar la perdida de data y la legibilidad.
from sklearn.cross_validation import KFold
from sklearn.model_selection import cross_val_score
from sklearn.cross_validation import cross_val_predict
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import Normalizer
from sklearn.pipeline import Pipeline
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.pipeline import FeatureUnion
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest
from sklearn.svm import SVC
transformers = []
transformers.append(( 'standardize' , StandardScaler()))
# # estimators.append(( 'normalizer' , Normalizer()))
# estimators.append(( 'lda' , LinearDiscriminantAnalysis()))
transf_union = FeatureUnion(transformers)
# model = Pipeline(estimators)
# create feature union
features = []
features.append(('transf_union', transf_union))
features.append(( 'pca', PCA(n_components=3)))
features.append(( 'select_best' , SelectKBest(k=3)))
feature_union = FeatureUnion(features)
# create pipeline
estimators = []
estimators.append(( 'feature_union' , feature_union))
estimators.append(( 'logistic' , SVC()))
# estimators.append(('svm' , SVC(kernel="linear")))
model = Pipeline(estimators)
# evaluate pipeline
num_folds = 10
num_instances = len(X)
seed = 7
kfold = KFold(n=num_instances, n_folds=num_folds, random_state=seed)
results = cross_val_score(model, X, y, cv=kfold)
print('Score:', results.mean())
# predictions = cross_val_predict(model, X, y)
import numpy as np
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
Y_test = encoder.fit_transform(Y_test)
predictions = encoder.fit_transform(predictions)
#Ploteando
y_test = Y_test
y_test = list(y_test)
predictions = list(predictions)
plt.scatter(y_test, predictions, alpha=0.3, color='green', s=300)
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'k--', color='darkgreen',lw=4,
label= 'X: Real value, Y: Predicted value. Score = '+str(results.mean()*100)+'%')
plt.xticks(np.arange(0,3), tuple(y.unique()), rotation=25,
fontsize = 10)
plt.yticks(np.arange(0,3), tuple(y.unique()), rotation=29,
fontsize = 10)
plt.legend(loc='best')
plt.grid(True)